package com.zzh.depth3dviewer;

import android.opengl.GLSurfaceView;
import android.util.Log;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class DepthImageRender implements GLSurfaceView.Renderer {
    // 最大深度
    public static float depthMax = 0;
    // 最小非0深度
    public static float depthMin = 0;
    //TODO 根据情况设置
    public static final int imgWidth = 720;
    //TODO 根据情况设置
    public static final int imgHeight = 1280;
    // 根据情况设置
    //TODO x、y轴坐标的范围(0.0~1.0)*imgXYMagnification
    public static final int imgXYMagnification = 6;

    // 定义图像的顶点，形如(x,y,z,x,y,z...)，长度为图像宽*高*3
    private float[] imageVertices;
    // 定义顶点的颜色，每个顶点的颜色长度为3，形如（r,g,b,a,r,g,b,a），长度为图像宽*高*4
    private int[] imageColors;
    // 定义Open GL ES绘制所需要的Buffer对象
    FloatBuffer imageVerticesBuffer;
    IntBuffer imageColorsBuffer;
    private boolean isAntiClockDir = true;
    // 图像旋转角度
    private float rotate = 0.0f;
    // 渐变色数组的长度
    private final int gradientColorsLength = 256*4;
    // 红--黄--绿--蓝渐变色中的红色分量
    private int[] gradientColorsR = new int[gradientColorsLength];
    // 红--黄--绿--蓝渐变色中的绿色分量
    private int[] gradientColorsG = new int[gradientColorsLength];
    // 红--黄--绿--蓝渐变色中的蓝色分量
    private int[] gradientColorsB = new int[gradientColorsLength];

    // 初始化时分4阶段，各阶段颜色值变化
    // 红色分量从65535-65535-->65535-0-->0-0-->0-0
    // 绿色分量从0-65535-->65535-65535-->65535-65535-->65535-0
    // 蓝色分量从0-0-->0-0-->0-65535-->65535-65535
    // 分量起始值和变化趋势（0：不变，1：上升，2：下降）
    private int[][] rgbWeightStart = new int[][]{{65535, 65535, 0, 0}, {0, 65535, 65535, 65535}, {0, 0, 0, 65535}};
    private int[][] rgbTrend = new int[][]{{0, 2, 0, 0}, {1, 0, 0, 2}, {0, 0, 1, 0}};
    // 颜色渐变跨度差
    private final int rgbTrendGap = 255;
    // TODO 也可设置成 GL10.GL_LINE_STRIP，效果会有所区别
    public static int depthDrawTypeDefault = GL10.GL_POINTS;
    public static int depthDrawType = depthDrawTypeDefault;

    public DepthImageRender() {
        generateGradientColors();
    }

    public void initImgVertices(byte[] rawFileBytes) {
        this.imageVertices = getImageDepthVertices(rawFileBytes, imgWidth, imgHeight);
        imageColors = new int[imageVertices.length * 4 / 3];
        int pixelIndex = 0;
        // 根据情况设置
        // TODO z轴放大倍数(深度值范围：(0.0~1.0)*depthMagnify)，倍数大，立体感强
        int depthMagnify = 4;
        float depthGap = depthMax - depthMin;
        Log.i("TAG", "depthMax=" + depthMax + ", depthMin=" + depthMin);
        if (depthGap <= 0.0f) {
            depthGap = 1.0f;
        }
        for (int i = 0; i < imageColors.length; i += 4) {
            // 直接从数据读取的深度值
            float rawDepth = imageVertices[2 + pixelIndex * 3];
            // 转化后的z轴的值
            float verticesZDepth;
            if (rawDepth > 0.0f) {
                //做个转换，深度值小的，z轴数据更大。深度大的，z轴数据更小。在坐标系中才能显示正常立体图
                verticesZDepth = (1.0f - (((float) (rawDepth - depthMin)) / depthGap)) * depthMagnify;
                //重新赋值
                imageVertices[2 + pixelIndex * 3] = verticesZDepth;
            }
//            Log.e("TAG", "depth222=" + verticesZDepth);
            int colorR = 0x0, colorG = 0x0, colorB = 0x0, colorA = 0x0;
            int[] colors = getRGBAByDepth(rawDepth, depthMin, depthMax);
            if (colors != null && colors.length > 3) {
                colorR = colors[0];
                colorG = colors[1];
                colorB = colors[2];
                colorA = colors[3];
            }
            imageColors[i] = colorR;
            imageColors[i + 1] = colorG;
            imageColors[i + 2] = colorB;
            imageColors[i + 3] = colorA;
            pixelIndex++;
        }
        // 将立方体的顶点位置数据数组包装成FloatBuffer
        imageVerticesBuffer = BufferUtil.floatBufferUtil(imageVertices);
        imageColorsBuffer = BufferUtil.intBufferUtil(imageColors);
    }

    private void generateGradientColors() {
        // 共4阶段，各阶段颜色值变化
        //红色分量从65535-65535-->65535-0-->0-0-->0-0
        //绿色分量从0-65535-->65535-65535-->65535-65535-->65535-0
        //蓝色分量从0-0-->0-0-->0-65535-->65535-65535
        for (int stage = 0; stage < 4; stage++) {
            int rWS = rgbWeightStart[0][stage];
            int rT = rgbTrend[0][stage];
            int gWS = rgbWeightStart[1][stage];
            int gT = rgbTrend[1][stage];
            int bWS = rgbWeightStart[2][stage];
            int bT = rgbTrend[2][stage];
            for (int color = 0; color < 256; color++) {
                if (rT == 1) {
                    rWS += rgbTrendGap;
                } else if (rT == 2) {
                    rWS -= rgbTrendGap;
                }
                gradientColorsR[stage * 256 + color] = rWS;
                if (gT == 1) {
                    gWS += rgbTrendGap;
                } else if (gT == 2) {
                    gWS -= rgbTrendGap;
                }
                gradientColorsG[stage * 256 + color] = gWS;
                if (bT == 1) {
                    bWS += rgbTrendGap;
                } else if (bT == 2) {
                    bWS -= rgbTrendGap;
                }
                gradientColorsB[stage * 256 + color] = bWS;
            }
        }
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        gl.glDisable(GL10.GL_DITHER);//关闭防抖
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT//设置透视修正
                , GL10.GL_FASTEST);
        gl.glClearColor(0, 0, 0, 0);
        gl.glShadeModel(GL10.GL_SMOOTH);// 设置为阴影平滑模式
        gl.glEnable(GL10.GL_DEPTH_TEST);// 启用深度测试
        gl.glDepthFunc(GL10.GL_LEQUAL);// 设置深度测试的类型
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        gl.glViewport(0, 0, width, height); //设置3D视窗的大小及位置
        gl.glMatrixMode(GL10.GL_PROJECTION); //设置矩阵模式设为投影矩阵
        gl.glLoadIdentity(); //初始化单位矩阵
        float ratio = (float) width / height; //计算视窗宽高比
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);//设置视窗空间大小
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);//启用vertex shader 数据
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);//启用fragment shader数据
        gl.glMatrixMode(GL10.GL_MODELVIEW);//设置当前矩阵堆栈为模型堆栈

        gl.glLoadIdentity();
        // 跟图像缩放有关
        gl.glTranslatef(0.0f, 0.0f, -5.0f);
        // 沿着(0,1,0)向量为轴的方向旋转
        //TODO 旋转相关，若不旋转，则第一个参数设置成0.0f
        gl.glRotatef(rotate/*0.0f*/, 0.0f, 1.0f, 0.0f);
        // 设置顶点的位置数据
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, imageVerticesBuffer);
        // 设置顶点的颜色数据
        gl.glColorPointer(4, GL10.GL_FIXED, 0, imageColorsBuffer);
        // 第一个参数如果是 GL10.GL_LINE_STRIP 则点会相互连接（柱状图像），GL10.GL_POINTS 只画点
        gl.glDrawArrays(depthDrawType, 0, imageVerticesBuffer.capacity() / 3);
        gl.glFinish();//绘制结束
        gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        if (isAntiClockDir) {
            if (rotate < 78.0f)
                rotate += 2.0f;
            if (rotate >= 78.0f) {
                isAntiClockDir = false;
            }
        } else {
            if (rotate >= 2.0f)
                rotate -= 2.0f;
            if (rotate <= 0.0f) {
                isAntiClockDir = true;
            }
        }
    }

    private int[] getRGBAByDepth(float rawDepth, float minDepth, float maxDepth) {
        // rgba color
        int r = 0x0, g = 0x0, b = 0x0, a = 0x0;
        if (rawDepth <= 0.0f || minDepth <= 0.0f || maxDepth <= 0.0f) {
            return new int[] {r, g, b, a};
        }
        // 深度分层的层数
        int depthLayerNum = (int) (maxDepth - minDepth);
        // 当前深度所在的层
        int curDepthRelativeLayer = (int) (rawDepth - minDepth);
        // 渐变色每一层的索引跨度
        int gradientColorsLayerGap = gradientColorsLength / depthLayerNum;
        if (gradientColorsLayerGap * curDepthRelativeLayer >= 0
                && gradientColorsLayerGap * curDepthRelativeLayer < gradientColorsLength) {
            r = gradientColorsR[gradientColorsLayerGap * curDepthRelativeLayer];
            g = gradientColorsG[gradientColorsLayerGap * curDepthRelativeLayer];
            b = gradientColorsB[gradientColorsLayerGap * curDepthRelativeLayer];
        }

        return new int[] {r, g, b, a};
    }

    /**
     * @param personBytes 原图的字节数组，每个像素16位存储
     * @param w 图像宽度
     * @param h 图像高度
     * */
    public float[] getImageDepthVertices(byte[] personBytes, int w, int h) {
        // 每个像素占3个长度
        float[] vertices = null;
        if (personBytes != null && personBytes.length > 0) {
            vertices = new float[personBytes.length * 3 / 2];
//            Log.i("tag", "personBytes length=" + personBytes.length);
//            Log.i("tag", "vertices length=" + vertices.length);
            int verticesIndex = 0;
            for (int row = 0; row < h; row++) {
                for (int col = 0; col < w; col++) {
                    // opengl坐标，X轴向右，右手定则，Y向上，Z向外
                    // x轴的值要进行缩小
                    vertices[verticesIndex] = (float) (((float) col * imgXYMagnification / w) - imgXYMagnification / 2);
                    // y轴的值要进行缩小
                    vertices[verticesIndex + 1] = (float) ((-((float) row * imgXYMagnification / h)) + imgXYMagnification / 2);
                    int firsBytesIndex = ((w * row) + col) * 2;
                    // 根据情况设置
                    //TODO 根据深度存储方式去读取深度值，本例为16位小端存储的读取方式
                    float depth = (float) ((personBytes[firsBytesIndex] & 0xFF)
                            | ((personBytes[firsBytesIndex + 1] << 8) & 0xFF00));
                    // 获取深度最大、最小值(非0)
                    if (depth != 0) {
                        if (depthMin == 0) {
                            depthMin = depth;
                        } else if (depth < depthMin) {
                            depthMin = depth;
                        }
                    }
                    if (depth > depthMax) {
                        depthMax = depth;
                    }
                    vertices[verticesIndex + 2] = depth;
                    verticesIndex += 3;
                }
            }
        }
        return vertices;
    }
}
